C.S.Burner 🪙 ━►🔥GIT Node
Node / reticulum_mirror / MeshChatX / files / meshchatx / src / frontend / components / tools / BotsPage.vue
Displaying Raw • Download
meshchatx/src/frontend/components/tools/BotsPage.vue 9ec2a88817819d433c5d68dbec0ca070ca989b91 (9ec2a888) Text, 28.22 KB
T8b949e<!-- SPDX-License-Identifier: 0BSD -->
Tff7b72<templateTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col flex-1 overflow-hidden min-w-0 bg-slate-50 dark:bg-zinc-950Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 overflow-y-auto w-full px-3 sm:px-4 md:px-5 lg:px-8 py-4 sm:py-6 pb-[max(1.5rem,env(safe-area-inset-bottom))]Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-8 w-full max-w-4xl mx-autoTa5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-2 border-b border-gray-200 dark:border-zinc-800 pb-6Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-xs uppercase tracking-wide text-gray-500 dark:text-gray-400Ta5d6ff"Tff7b72>
{{ Te6edf3$t("bots.bot_framework") }}
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-2xl font-semibold text-gray-900 dark:text-whiteTa5d6ff"Tff7b72>{{ Te6edf3$t("bots.title") }}Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-600 dark:text-gray-300Ta5d6ff"Tff7b72>
{{ Te6edf3$t("bots.description") }}
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-6Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fftext-lg font-semibold text-gray-900 dark:text-whiteTa5d6ff"Tff7b72>
{{ Te6edf3$t("bots.create_new_bot") }}
Tff7b72</h3>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3v-for=Ta5d6ff"Ta5d6fftemplate in templatesTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6fftemplate.idTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrelative rounded-lg border border-gray-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 p-4 hover:border-blue-400 dark:hover:border-blue-600 transition cursor-pointer flex flex-col justify-between min-h-[140px] pr-12Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffselectTemplate(template)Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fffont-bold text-gray-900 dark:text-whiteTa5d6ff"Tff7b72>{{ template.name }}Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-600 dark:text-gray-400 mt-1Ta5d6ff"Tff7b72>
{{ template.description }}
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute bottom-3 right-3 p-2 rounded-lg text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 transition-colors pointer-events-noneTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffchevron-rightTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-6Ta5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-lg border border-dashed border-gray-300 dark:border-zinc-700 bg-gray-50/50 dark:bg-zinc-900/50 p-4 flex flex-col items-center justify-center min-h-[140px] opacity-70Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffp-2 bg-gray-100 dark:bg-zinc-800 rounded-lg mb-2Ta5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3icon-name=Ta5d6ff"Ta5d6ffplusTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-6 text-gray-400 dark:text-gray-500Ta5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-medium text-gray-500 dark:text-gray-400 text-centerTa5d6ff"Tff7b72>
{{ Te6edf3$t("bots.more_bots_coming") }}
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fftext-lg font-semibold text-gray-900 dark:text-whiteTa5d6ff"Tff7b72>
{{ Te6edf3$t("bots.saved_bots") }}
Tff7b72</h3>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffbots.length === 0Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-500 italicTa5d6ff"Tff7b72>
{{ Te6edf3$t("bots.no_bots_running") }}
Tff7b72</div>
Tff7b72<div v-else Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-2 sm:space-y-3Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3v-for=Ta5d6ff"Ta5d6ffbot in botsTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffbot.idTa5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff[
Ta5d6ff'Ta5d6ffrelative rounded-lg border border-gray-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 p-3 sm:p-4Ta5d6ff'Ta5d6ff,
editingBotId === bot.id ? Ta5d6ff'Ta5d6ffpr-28 sm:pr-40Ta5d6ff'Ta5d6ff : Ta5d6ff'Ta5d6ffpr-10 sm:pr-12Ta5d6ff'Ta5d6ff,
]Ta5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute top-2 right-2 flex flex-wrap items-center justify-end gap-0.5 z-10 max-w-[min(100%,calc(100%-2rem))]Ta5d6ff"
Tff7b72>
Tff7b72<button
Te6edf3v-if=Ta5d6ff"Ta5d6fflxmfAddressFor(bot)Ta5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.chat_with_botTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffopenChatWithBot(bot)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffmessage-textTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3v-if=Ta5d6ff"Ta5d6ffbot.runningTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 dark:text-gray-400 hover:text-amber-600 dark:hover:text-amber-400 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.force_announceTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffforceAnnounce(bot)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffbullhornTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3v-if=Ta5d6ff"Ta5d6ffbot.runningTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.restart_botTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffrestartExisting(bot)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffrefreshTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3v-if=Ta5d6ff"Ta5d6ffbot.runningTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 dark:text-gray-400 hover:text-red-600 dark:hover:text-red-400 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.stop_botTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffstopBot(bot.id)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffstopTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3v-if=Ta5d6ff"Ta5d6ff!bot.runningTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 dark:text-gray-400 hover:text-emerald-600 dark:hover:text-emerald-400 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.start_botTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffstartExisting(bot)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffplayTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.export_identityTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffexportIdentity(bot.id)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffexportTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 dark:text-gray-400 hover:text-red-600 dark:hover:text-red-400 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.delete_botTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffdeleteBot(bot.id)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffdeleteTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-start gap-3 min-w-0Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffp-2 bg-blue-100 dark:bg-blue-900/30 rounded-lg shrink-0Ta5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3icon-name=Ta5d6ff"Ta5d6ffrobotTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-6 text-blue-600 dark:text-blue-400Ta5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0 flex-1 space-y-1.5 sm:pr-2Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-1 min-w-0Ta5d6ff"
Te6edf3:class=Ta5d6ff"
Ta5d6ff editingBotId === bot.id
? Ta5d6ff'Ta5d6ffmax-w-[min(100%,14rem)] sm:max-w-[16rem]Ta5d6ff'
Ta5d6ff : Ta5d6ff'Ta5d6ff'
Ta5d6ff Ta5d6ff"
Tff7b72>
Tff7b72<template Te6edf3v-if=Ta5d6ff"Ta5d6ffeditingBotId === bot.idTa5d6ff"Tff7b72>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffeditingNameDraftTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field text-xs py-1 h-8 px-2 min-w-0 flex-1 max-w-[10rem] sm:max-w-[12rem]Ta5d6ff"
Te6edf3maxlength=Ta5d6ff"Ta5d6ff256Ta5d6ff"
@keydown.enter.Te6edf3prevent=Ta5d6ff"Ta5d6ffsaveBotName(bot)Ta5d6ff"
@keydown.Te6edf3escape=Ta5d6ff"Ta5d6ffcancelEditNameTa5d6ff"
Tff7b72/>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-1 rounded-lg text-gray-500 hover:text-emerald-600 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 shrink-0Ta5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffcommon.saveTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffsaveBotName(bot)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffcheckTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-1 rounded-lg text-gray-500 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 shrink-0Ta5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffcommon.cancelTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffcancelEditNameTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffcloseTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72</template>
Tff7b72<template v-elseTff7b72>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fffont-bold text-gray-900 dark:text-white truncateTa5d6ff"Tff7b72>{{
bot.name
}}Tff7b72</span>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-1.5 rounded-lg text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 shrink-0Ta5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.edit_nameTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffstartEditName(bot)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffpencilTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72</template>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2 text-[11px] text-gray-600 dark:text-gray-300Ta5d6ff"
Tff7b72>
Tff7b72<span
Te6edf3class=Ta5d6ff"Ta5d6ffinline-block size-2 rounded-full shrink-0Ta5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ffbot.running ? Ta5d6ff'Ta5d6ffbg-emerald-500Ta5d6ff'Ta5d6ff : Ta5d6ff'Ta5d6ffbg-gray-400 dark:bg-gray-500Ta5d6ff'Ta5d6ff"
Tff7b72>Tff7b72</span>
Tff7b72<spanTff7b72>{{
bot.running ? Te6edf3$t("bots.status_running") : Te6edf3$t("bots.status_stopped")
}}Tff7b72</span>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-[11px] text-gray-500 dark:text-gray-400Ta5d6ff"Tff7b72>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fffont-semibold text-gray-600 dark:text-gray-300Ta5d6ff"Tff7b72>{{
Te6edf3$t("bots.lxmf_address")
}}Tff7b72</span>
Tff7b72<button
Te6edf3v-if=Ta5d6ff"Ta5d6fflxmfAddressFor(bot)Ta5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fffont-mono break-all text-left text-gray-800 dark:text-gray-200 hover:underlineTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffcopyLxmfAddress(bot)Ta5d6ff"
Tff7b72>
{{ lxmfAddressFor(bot) }}
Tff7b72</button>
Tff7b72<span v-elseTff7b72>{{ Te6edf3$t("bots.address_pending") }}Tff7b72</span>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-[11px] text-gray-500 dark:text-gray-400Ta5d6ff"Tff7b72>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fffont-semibold text-gray-600 dark:text-gray-300Ta5d6ff"Tff7b72>{{
Te6edf3$t("bots.last_announce")
}}Tff7b72</span>
Tff7b72<span Te6edf3v-if=Ta5d6ff"Ta5d6ffbot.last_announce_atTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffml-1.5Ta5d6ff"Tff7b72>{{
formatRelativeSince(bot.last_announce_at)
}}Tff7b72</span>
Tff7b72<span Te6edf3v-else-if=Ta5d6ff"Ta5d6fflxmfAddressFor(bot)Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffml-1.5Ta5d6ff"Tff7b72>{{
Te6edf3$t("bots.never_announced")
}}Tff7b72</span>
Tff7b72<span v-else Te6edf3class=Ta5d6ff"Ta5d6ffml-1.5Ta5d6ff"Tff7b72>—Tff7b72</span>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-gray-400Ta5d6ff"Tff7b72>
{{ bot.template_id || bot.template }}
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffselectedTemplateTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fffixed inset-0 z-[100] flex items-end sm:items-center justify-center p-0 sm:p-4 bg-black/50Ta5d6ff"
@click.Te6edf3self=Ta5d6ff"Ta5d6ffselectedTemplate = nullTa5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffw-full sm:max-w-md rounded-t-2xl sm:rounded-lg border border-gray-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 p-4 sm:p-6 space-y-4 max-h-[90vh] overflow-y-autoTa5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex justify-between items-start gap-2Ta5d6ff"Tff7b72>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fftext-lg sm:text-xl font-bold text-gray-900 dark:text-white pr-2Ta5d6ff"Tff7b72>
{{ Te6edf3$t("bots.start_bot") }}: {{ selectedTemplate.name }}
Tff7b72</h3>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100/80 dark:hover:bg-zinc-800/80Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffselectedTemplate = nullTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffcloseTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<label Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>{{ Te6edf3$t("bots.bot_name") }}Tff7b72</label>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewBotNameTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3:placeholder=Ta5d6ff"Ta5d6ffselectedTemplate.nameTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-600 dark:text-gray-400Ta5d6ff"Tff7b72>
{{ selectedTemplate.description }}
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex justify-end gap-2 pt-2Ta5d6ff"Tff7b72>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100/80 dark:hover:bg-zinc-800/80Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffselectedTemplate = nullTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffcloseTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-6Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 hover:text-emerald-600 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 disabled:opacity-40Ta5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffisStartingTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffstartBotTa5d6ff"
Tff7b72>
Tff7b72<span
Te6edf3v-if=Ta5d6ff"Ta5d6ffisStartingTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinline-block w-5 h-5 border-2 border-current border-t-transparent rounded-full animate-spinTa5d6ff"
Tff7b72>Tff7b72</span>
Tff7b72<MaterialDesignIcon v-else Te6edf3icon-name=Ta5d6ff"Ta5d6ffcheckTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-6Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>
Tff7b72<scriptTff7b72>
import ToastUtils from "../../js/ToastUtils";
import MaterialDesignIcon from "../MaterialDesignIcon.vue";
export default {
name: "BotsPage",
components: {
MaterialDesignIcon,
},
data() {
return {
bots: [],
templates: [],
selectedTemplate: null,
newBotName: "",
isStarting: false,
loading: true,
refreshInterval: null,
relativeTimerTick: 0,
relativeTimerInterval: null,
editingBotId: null,
editingNameDraft: "",
};
},
mounted() {
this.getStatus();
this.refreshInterval = setInterval(this.getStatus, 5000);
this.relativeTimerInterval = setInterval(() => {
this.relativeTimerTick += 1;
}, 1000);
},
beforeUnmount() {
if (this.refreshInterval) {
clearInterval(this.refreshInterval);
}
if (this.relativeTimerInterval) {
clearInterval(this.relativeTimerInterval);
}
},
methods: {
async getStatus() {
try {
const response = await window.api.get("/api/v1/bots/status");
this.bots = response.data.status.bots || [];
this.templates = response.data.templates;
this.loading = false;
} catch (e) {
console.error(e);
}
},
selectTemplate(template) {
this.selectedTemplate = template;
this.newBotName = template.name;
},
async startBot() {
if (this.isStarting) return;
this.isStarting = true;
try {
await window.api.post("/api/v1/bots/start", {
template_id: this.selectedTemplate.id,
name: this.newBotName,
});
ToastUtils.success(this.$t("bots.bot_started"));
this.selectedTemplate = null;
this.getStatus();
} catch (e) {
console.error(e);
ToastUtils.error(e.response?.data?.message || this.$t("bots.failed_to_start"));
} finally {
this.isStarting = false;
}
},
async stopBot(botId) {
try {
await window.api.post("/api/v1/bots/stop", { bot_id: botId });
ToastUtils.success(this.$t("bots.bot_stopped"));
this.getStatus();
} catch (e) {
console.error(e);
ToastUtils.error(this.$t("bots.failed_to_stop"));
}
},
async startExisting(bot) {
try {
await window.api.post("/api/v1/bots/start", {
bot_id: bot.id,
template_id: bot.template_id || bot.template,
name: bot.name,
});
ToastUtils.success(this.$t("bots.bot_started"));
this.getStatus();
} catch (e) {
console.error(e);
ToastUtils.error(e.response?.data?.message || this.$t("bots.failed_to_start"));
}
},
async restartExisting(bot) {
try {
await window.api.post("/api/v1/bots/restart", { bot_id: bot.id });
ToastUtils.success(this.$t("bots.bot_started"));
this.getStatus();
} catch (e) {
console.error(e);
ToastUtils.error(e.response?.data?.message || this.$t("bots.failed_to_start"));
}
},
async deleteBot(botId) {
if (!confirm(this.$t("common.delete_confirm"))) return;
try {
await window.api.post("/api/v1/bots/delete", { bot_id: botId });
ToastUtils.success(this.$t("bots.bot_deleted"));
if (this.editingBotId === botId) {
this.cancelEditName();
}
this.getStatus();
} catch (e) {
console.error(e);
ToastUtils.error(this.$t("bots.failed_to_delete"));
}
},
exportIdentity(botId) {
window.open(`/api/v1/bots/export?bot_id=${botId}`, "_blank");
},
startEditName(bot) {
this.editingBotId = bot.id;
this.editingNameDraft = bot.name || "";
},
cancelEditName() {
this.editingBotId = null;
this.editingNameDraft = "";
},
async saveBotName(bot) {
if (this.editingBotId !== bot.id) {
return;
}
const name = (this.editingNameDraft || "").trim();
if (!name) {
ToastUtils.error(this.$t("bots.name_required"));
return;
}
try {
await window.api.patch("/api/v1/bots/update", {
bot_id: bot.id,
name,
});
ToastUtils.success(this.$t("bots.bot_renamed"));
this.cancelEditName();
this.getStatus();
} catch (e) {
console.error(e);
ToastUtils.error(e.response?.data?.message || this.$t("bots.rename_failed"));
}
},
async forceAnnounce(bot) {
try {
await window.api.post("/api/v1/bots/announce", { bot_id: bot.id });
ToastUtils.success(this.$t("bots.announce_triggered"));
this.getStatus();
} catch (e) {
console.error(e);
ToastUtils.error(e.response?.data?.message || this.$t("bots.announce_failed"));
}
},
lxmfAddressFor(bot) {
const raw = bot.lxmf_address || bot.full_address;
if (!raw || typeof raw !== "string") {
return "";
}
const h = raw.trim().toLowerCase();
return h.length === 32 && /^[0-9a-f]+$/.test(h) ? h : "";
},
openChatWithBot(bot) {
const h = this.lxmfAddressFor(bot);
if (!h) {
return;
}
const routeName = this.$route?.meta?.isPopout ? "messages-popout" : "messages";
this.$router.push({ name: routeName, params: { destinationHash: h } });
},
copyLxmfAddress(bot) {
const h = this.lxmfAddressFor(bot);
if (!h) {
return;
}
navigator.clipboard.writeText(h);
ToastUtils.success(this.$t("translator.copied_to_clipboard"));
},
formatRelativeSince(iso) {
if (!iso) {
return "";
}
let t;
try {
t = new Date(iso).getTime();
} catch {
return String(iso);
}
if (Number.isNaN(t)) {
return String(iso);
}
const now = Date.now() + 0 * this.relativeTimerTick;
let sec = Math.floor((now - t) / 1000);
if (sec Tff7b72< 0) {
Te6edf3sec = Ta5d6ff0;
}
if (sec < 60) {
return `${sec}s`;
}
const Te6edf3min = Ta5d6ffMath.floor(sec / 60);
if (min < 60) {
return Te6edf3min =Ta5d6ff== 1 ? `${min}m` : `${min}m`;
}
const Te6edf3h = Ta5d6ffMath.floor(min / 60);
if (h < 24) {
return Te6edf3h =Ta5d6ff== 1 ? `${h}h` : `${h}h`;
}
const Te6edf3d = Ta5d6ffMath.floor(h / 24);
if (d < 30) {
return Te6edf3d =Ta5d6ff== 1 ? "1 day" : `${d} days`;
}
const Te6edf3mo = Ta5d6ffMath.floor(d / 30);
if (d < 365) {
return Te6edf3mo =Ta5d6ff== 1 ? "1 month" : `${mo} months`;
}
const Te6edf3y = Ta5d6ffMath.floor(d / 365);
return Te6edf3y =Ta5d6ff== 1 ? "1 year" : `${y} years`;
},
},
};
</scriptTff7b72>
Tff7b72<style scopedTff7b72>
.glass-label {
@apply block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-1;
}
Tff7b72</style>
Served by rngit 1.5.2 - Generated in 0.05s